#pragma once
#include <Windows.h>
#include <WindowsX.h>
#include <Commctrl.h>
#include <usp10.h>
#include <WinError.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <DxErr.h>
#include "tstring.h"
#include <vector>
#include <map>
using namespace std;

#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"d3dx9.lib")
#pragma comment(lib,"DxErr.lib")
#pragma comment(lib,"Usp10.lib")

HRESULT MUTGetErrorMessage(DWORD dwErrorCode,TString& strError);

//Error Handling Macros
#define MUTWinErrorTrace(c) {TString __s;\
	MUTGetErrorMessage(c,__s);\
		DXTrace(__FILE__,(DWORD)__LINE__,HRESULT_FROM_WIN32(c),__s,true);}
#define MUTDXErrorTrace(hr,str) DXTrace(__FILE__,(DWORD)__LINE__,hr,str,true)
#define MUTErrorTrace(str) MUTDXErrorTrace(0,str)

#define MUTWS_WINDOWED WS_SYSMENU|WS_BORDER|WS_CAPTION
#define MUTWS_EX_WINDOWED WS_EX_APPWINDOW
#define MUTWS_FULLSCREEN WS_POPUP
#define MUTWS_EX_FULLSCREEN WS_EX_APPWINDOW|WS_EX_TOPMOST
HRESULT MUTCreateWindow(HINSTANCE hInst, TString strWindowName, int iWidth, int iHeight, bool bWindowed);

HRESULT MUTD3DCreate9();
HRESULT MUTCreateDevice();
HRESULT MUTD3DRelease();
HRESULT MUTResetDevice();

typedef HRESULT (*ONCREATEDEVICE)(LPDIRECT3DDEVICE9);
HRESULT MUTSetOnCreateDevice(ONCREATEDEVICE func);
typedef HRESULT (*ONRESETDEVICE)(LPDIRECT3DDEVICE9);
HRESULT MUTSetOnResetDevice(ONRESETDEVICE func);
typedef HRESULT (*ONRELEASEDEVICE)();
HRESULT MUTSetOnReleaseDevice(ONRELEASEDEVICE);
typedef HRESULT (*ONRENDER)(LPDIRECT3DDEVICE9);
HRESULT MUTSetOnRender(ONRENDER func);
typedef HRESULT (*ONWNDPROC)(HWND ,UINT ,WPARAM ,LPARAM);
HRESULT MUTSetOnWndProc(ONWNDPROC func);

HRESULT MUTToggleFullscreen();
LRESULT CALLBACK MUTWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam);

HRESULT MUTMainLoop();
HRESULT MUTD3DMain();

void MUTLog( char* str );

#define DECL_GET_SET(t,x) t x; inline t Get##x(){return x;}; inline void Set##x(t v){ x=v; };
#define D3DPP_GET_SET(t,x) inline t GetD3DPP_##x(){return d3dpp.##x;} inline void SetD3DPP_##x(t v){ d3dpp.##x=v; };

class MUTSTATE{
public:
	MUTSTATE(){ ZeroMemory(this,sizeof(MUTSTATE)); }
	DECL_GET_SET(HWND, hwnd)
	DECL_GET_SET(HINSTANCE, Instance)
	DECL_GET_SET(BOOL, Windowed)
	DECL_GET_SET(DWORD, Style)
	DECL_GET_SET(DWORD, StyleEx)
	DECL_GET_SET(int, Width)
	DECL_GET_SET(int, Height)
	DECL_GET_SET(POINT, PreMousePos)
	DECL_GET_SET(POINT, MousePos)

	//D3D
	DECL_GET_SET(float,ElapsedTime)
	DECL_GET_SET(LPDIRECT3D9, D3DObj)
	DECL_GET_SET(D3DDEVTYPE, DevType)
	DECL_GET_SET(DWORD, MaxMultiSampleQuality)
	DECL_GET_SET(DWORD, VertexProcessing)
	DECL_GET_SET(LPDIRECT3DDEVICE9, Device)
	D3DPRESENT_PARAMETERS d3dpp;
	D3DPRESENT_PARAMETERS* GetD3DPP(){ return &d3dpp; }
	D3DPP_GET_SET(UINT, BackBufferWidth)
	D3DPP_GET_SET(UINT, BackBufferHeight)
	D3DPP_GET_SET(D3DFORMAT, BackBufferFormat)
	D3DPP_GET_SET(UINT, BackBufferCount)
	D3DPP_GET_SET(D3DMULTISAMPLE_TYPE, MultiSampleType)
	D3DPP_GET_SET(DWORD, MultiSampleQuality)
	D3DPP_GET_SET(D3DSWAPEFFECT, SwapEffect)
	D3DPP_GET_SET(HWND, hDeviceWindow)
	D3DPP_GET_SET(BOOL, Windowed)
	D3DPP_GET_SET(BOOL, EnableAutoDepthStencil)
	D3DPP_GET_SET(D3DFORMAT, AutoDepthStencilFormat)
	D3DPP_GET_SET(DWORD, Flags)
	D3DPP_GET_SET(UINT, FullScreen_RefreshRateInHz)
	D3DPP_GET_SET(UINT, PresentationInterval)
};
MUTSTATE* MUTState();



enum MUTRES_TYPE{
	MUTRES_VB = 0,
	MUTRES_IB,
	MUTRES_TEXTURE
};
struct REQUEST_VERTEXDECLARATION{
	LPD3DVERTEXELEMENT9  elem;
	LPDIRECT3DVERTEXDECLARATION9* decl;
};
struct REQUEST_VERTEXBUFFER{
	DWORD dwLength;
	DWORD FVF;
	LPDIRECT3DVERTEXBUFFER9* vb;
};
struct REQUEST_INDEXBUFFER{
	DWORD dwLength;
	D3DFORMAT fmt;
	LPDIRECT3DINDEXBUFFER9* ib;
};
struct REQUEST_TEXTUREFROMFILE{
	TString fn;
	LPDIRECT3DTEXTURE9* tex;
};
struct REQUEST_LOCK{
	MUTRES_TYPE type;
	LPVOID ptr;
	DWORD flag;
	LPVOID* out;
};
struct REQUEST_UNLOCK{
	MUTRES_TYPE type;
	LPVOID ptr;
};
struct REQUEST_RELEASE{
	MUTRES_TYPE type;
	LPVOID ptr;
};
struct REQUEST_BATCH{
	BOOL bReady;
	BOOL bDone;
	vector<REQUEST_VERTEXDECLARATION> vreqDecl;
	vector<REQUEST_VERTEXBUFFER> vreqVB;
	vector<REQUEST_INDEXBUFFER> vreqIB;
	vector<REQUEST_TEXTUREFROMFILE> vreqTexFromFile;
	vector<REQUEST_LOCK> vreqLock;
	vector<REQUEST_UNLOCK> vreqUnlock;
	vector<REQUEST_RELEASE> vreqRelease;
};
HRESULT CheckThreadRequest();
void MUTRequestSetup();
void MUTRequestBegin( HANDLE hEvent );
void MUTRequestVertexDeclaration( HANDLE hEvent, LPD3DVERTEXELEMENT9 elem, LPDIRECT3DVERTEXDECLARATION9& decl );
void MUTRequestVertexBuffer( HANDLE hEvent, DWORD dwLength, DWORD FVF, LPDIRECT3DVERTEXBUFFER9& vb );
void MUTRequestIndexBuffer( HANDLE hEvent, DWORD dwLength, D3DFORMAT fmt, LPDIRECT3DINDEXBUFFER9& ib );
void MUTRequestTextureFromFile( HANDLE hEvent, LPCWSTR fn, LPDIRECT3DTEXTURE9& tex );
void MUTRequestLock( HANDLE hEvent, MUTRES_TYPE type, LPVOID ptr, DWORD flag, LPVOID& out );
void MUTRequestUnlock( HANDLE hEvent, MUTRES_TYPE type, LPVOID ptr );
void MUTRequestEnd( HANDLE hEvent );
void MUTRequestRelease( HANDLE hEvent, MUTRES_TYPE type, LPVOID ptr );


struct FULLSCREENVERTEX{
	D3DXVECTOR4 pos;
	D3DXVECTOR2 uv;
};

void DrawFullScreenRect( LPDIRECT3DDEVICE9 pDevice );